      <h1 class="entry-title entry-title-single" itemprop="headline">ABAP 开发系列(08): SAP Open SQL</h1>

    <div class="entry-content entry-content-single entry-content-aside" itemprop="text">
      <p>在前面我们介绍<span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/sap-abap" title="ABAP" target="_blank">ABAP</a></span> WorkBench时了解到 <span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/sap" title="SAP" target="_blank">SAP</a></span> 的三层架构：</p>
<p>  &#8211; <b>表现层</b>（Presentation），<b>应用层</b>（Application），<b>数据层</b>（Database）</p>
<p><span class='wp_keywordlink_affiliate'><a href="http://www.sapjx.com/tag/open-sql" title="查看Open SQL中的全部文章" target="_blank">Open SQL</a></span>就发生在 Application 和 Database 之间；</p>
<p><span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/sap" title="SAP" target="_blank">SAP</a></span> Application将发送的<span class='wp_keywordlink_affiliate'><a href="http://www.sapjx.com/tag/open-sql" title="查看Open SQL中的全部文章" target="_blank">Open SQL</a></span>请求，与Database进行匹配，转换成接受的SQL语句，</p>
<p>DB再执行SQL，将结果返回给Application。</p>
<p>&nbsp;<br />
<span id="more-316"></span><br />
<b>1.Open SQL </b><b>概述</b></p>
<p>在<span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/sap-abap" title="ABAP" target="_blank">ABAP</a></span>程序中有两种方式访问数据库：<b>Open SQL</b> 和 <b>Native SQL</b></p>
<p>1.1 Open SQL 是完全集成到 <span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/sap-abap" title="ABAP" target="_blank">ABAP</a></span>语言中的Standard SQL的子集。它通过数据库接口将 Open SQL 转换成 Standard SQL，</p>
<p>所以它可以用相同的方式访问不同的数据库系统，然后由该接口把SAP的Open SQL自动转换为相应数据库的特定SQL语句。</p>
<p>Open SQL 包含Standard SQL的数据操作语言Data Manipulation Language (DML)部分，允许你读取和修改数据。</p>
<p>Data Definition Language (DDL) 和 Data Control Language (DCL) 部分使用ABAP Dictionary处理。</p>
<p>&nbsp;</p>
<p>1.2 Native SQL 语法不会使用数据库接口进行转换，它直接访问数据库，所以一个SQL只能访问一个指定数据库，</p>
<p>且具有一定的安全风险，对SAP执行效率也会有较大影响，所以一般不推荐使用。</p>
<p><img src="http://www.sapjx.com/wp-content/uploads/2014/03/abap_08_data_interface.png" alt="abap_08_data_interface" width="601" height="251" class="alignnone size-full wp-image-513" /></p>
<p>&nbsp;</p>
<p><b>2. Open SQL 语句</b></p>
<p>Open SQL包含的DML語法有4种：<b>SELECT, INSERT, UPDATE, DELETE, MODIFY</b></p>
<p><b>SELECT</b>：  查询语法</p>
<p><b>INSERT</b>：  插入数据语法</p>
<p><b>UPDATE</b>：  更新数据语法</p>
<p><b>DELETE</b>：  删除数据语法</p>
<p><b>MODIFY</b>：  修改数据语法，相当于 INSERT 和 UPDATE</p>
<p>修改数据前会先在数据库查询是否存在该记录，如果存在则修改，不存在则插入新的数据</p>
<p><img src="http://www.sapjx.com/wp-content/uploads/2014/03/abap_08_Open_SQL.png" alt="abap_08_Open_SQL" width="670" height="270" class="alignnone size-full wp-image-317" /></p>
<p>Open SQL执行成功后，会返回代码 SY-SUBRC = 0, 如果失败 SY-SUBRC &lt;&gt; 0</p>
<p>除 <b>SELECT</b> 以外的命令，其他都会涉及到数据的修改，要谨慎使用。</p>
<p>在系统标准表中，我们只允许用 <b>SELECT</b> 命令去查询数据，其他命令语句只能用于自建表，</p>
<p>如若要修改系统标准表，只能通过系统标准程序去操作。</p>
<p>&nbsp;</p>
<p><b>3. SELECT </b></p>
<p>SELECT 命令包含如下从句：</p>
<p><b>SELECT</b>: 需要查询资料库指定表的那些列，是一行还是多行</p>
<p><b>INTO</b>:   查询的结果保存在哪里</p>
<p><b>FROM</b>:   从哪些表查询数</p>
<p><b>WHERE</b>:  指定查询条件</p>
<p><b>GROUP BY</b>: 以哪些栏位进行分組</p>
<p><b>ORDER BY</b>: 以哪些栏位进行排序.</p>
<p>&nbsp;</p>
<p>3.1 <b>SELECT SINGLE </b>命令</p>
<p><b>SELECT SINGLE </b>命令允许你查询一条记录 ,为了确保你查询的记录是唯一的，你必须在你的 WHERE 子句指定所有KEY值，</p>
<p>如若查询的记录不止一条，系统会返回代码 SY-SUBRC = 8，查询结果为空；</p>
<p><b style="color:red;">SELECT SINGLE &lt;F1&gt; &lt;F2&gt;  FROM &lt;dbtab&gt;</b></p>
<p><b style="color:red; padding-left:20px;">INTO &lt;work area&gt;</b></p>
<p><b style="color:red; padding-left:20px;">INTO (&lt;f1&gt;, &lt;f2&gt;, &lt;f3&gt; …  )</b></p>
<p><b style="color:red; padding-left:20px;">INTO CORRESPONDING FIELDS OF &lt;work area&gt;</b></p>
<p><b style="color:red; padding-left:20px;">WHERE  &lt;Key1&gt; &lt;op&gt;  AND &lt;Key2&gt; &lt;op&gt; …</b></p>
<p>&nbsp;</p>
<p>在INTO中使用 <b>CORRESPONDING FIELDS OF</b>,系统只填充与你的Work Area相同名称的栏位值；</p>
<p>执行成功返回代码 SY-SUBRC = 0,如果失敗SY-SUBRC &lt;&gt; 0。</p>
<p>示例：</p>
<pre class="brush: abap;">

DATA wa_spfli LIKE spfli.

SELECT SINGLE
   carrid connid airpfrom airpto
   FROM apfli
   INTO CORRESPONDING FIELDS OF wa_spfli
   WHERE carrid = 'LH' 
     AND connid = '400'.

</pre>
<p>&nbsp;</p>
<p><b>3.2 SELECT </b><b>查询多条记录</b></p>
<p>SELECT 查询多条记录有两种方式:</p>
<p>1.使用<b style="color:red;">SELECT … ENDSELECT</b>命令，进行循环处理；</p>
<p>2.一次性读取多笔记录到内表中，在 <b>INTO</b> 使用TABLE附加字</p>
<p>&nbsp;</p>
<p>第一种方式语法格式：</p>
<p><b style="color:red;">SELECT &lt;F1&gt; … &lt;Fn&gt;  FROM &lt;dbtab&gt;</b></p>
<p><b style="color:red; padding-left:20px;">INTO &lt;work area&gt;</b></p>
<p><b style="color:red; padding-left:20px;">INTO (&lt;f1&gt;, &lt;f2&gt;, &lt;f3&gt; …  )</b></p>
<p><b style="color:red; padding-left:20px;">INTO CORRESPONDING FIELDS OF &lt;work area&gt;</b></p>
<p><b style="color:red; padding-left:20px;">WHERE  &lt;tabfield&gt; &lt;operator&gt; &lt;value&gt;.</b></p>
<p><b style="color:red;">ENDSELECT.</b></p>
<p>&nbsp;</p>
<p>第二种方式语法格式：</p>
<p><b style="color:red;">SELECT  &lt;f1&gt; … &lt;fn&gt;  FROM &lt;dbtab&gt;</b></p>
<p><b style="color:red; padding-left:20px;">INTO TABLE &lt;itab&gt;</b></p>
<p><b style="color:red; padding-left:20px;">WHERE …</b></p>
<p>&nbsp;</p>
<p>示例：</p>
<pre class="brush: abap;">

DATA tab_spfli TYPE STANDARD TABLE OF spfli.
DATA wa_spfli  LIKE spfli.

SELECT carrid connid airpfrom airpto
   FROM apfli
   INTO wa_spfli
   WHERE carrid = 'LH'  
     AND connid = '400'.
ENDSELECT.

SELECT carrid connid airpfrom airpto
   FROM apfli
   INTO CORRESPONDING TABLE OF tab_spfli
   WHERE carrid = 'LH'
     AND connid = '400'.

</pre>
<p>&nbsp;</p>
<p>注意：使用 <b>SELECT… ENDSELECT</b> 语法中不能加入 <b>SINGLE、INTO CORRESPONDING FIELDS 、TABLE</b>等关键字，</p>
<p>中间可以使用 <b>CHECK</b> 语法来判断查询值。</p>
<p>&nbsp;</p>
<p><b>3.3 SELECT </b><b>附加选项</b></p>
<p><b style="color:red;">SINGLE [FOR UPDATE]</b>: 使用它会产生一个排他锁，其他程序不能查询此笔记录。</p>
<p>&nbsp;</p>
<p>示例：</p>
<pre class="brush: abap;">

* 星号表示查询所有栏位
SELECT SINGLE FOR UPDATE *
   FROM SCARR
   WHERE currcode = 'USD'.

WRITE : /  scarr-CARRID. 

</pre>
<p><b style="color:red;">DISTINCT</b>：查询非重复记录，需要使用ORDER BY排序</p>
<p>&nbsp;<br />
&nbsp;</p>
<p><b>3.4 SELECT</b><b>标准<span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/function" title="函数" target="_blank">函数</a></span></b></p>
<p>在使用 <b>SELECT</b> 语句时，可以直接使用Open SQL提供的标准<span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/function" title="函数" target="_blank">函数</a></span>进行相关操作，常用的标准<span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/function" title="函数" target="_blank">函数</a></span>有以下几种：</p>
<p><b>1.COUNT()</b>：  统计查询记录总数</p>
<p><b>2.SUM()</b>: 汇总某查询数字字段数量</p>
<p><b>3.AVG()</b>：计算某查询数字字段平均值</p>
<p><b>4.MAX()</b>：查询表中记录最大值</p>
<p><b>5.MIN()</b>:查询表中记录最小值</p>
<p>使用这些函数时，如果要包含其他栏位，必须使用<b>GROUP BY</b>命令</p>
<p>&nbsp;</p>
<p><b>3.5  FROM </b><b>子句</b></p>
<p>1. <b>FROM dbtab [</b><b style="color:red;">AS</b>  <b>alias]</b>  -  AS 附加字，用于对 Table另起别名</p>
<p>2. <b style="color:red;">JOIN </b><b>联合查询</b></p>
<p>JOIN的联合查询有两种， <b>INNER JOIN</b> 和 <b>LEFT/RIGHT [OUTER] JOIN</b></p>
<p>通过<b>ON</b> 语句关联2个或2个以上的表，且必须至少指定一个连接条件；在多个连接条件时，可以使用AND；</p>
<p><b>ON</b>左右的条件只能使用 = (EQ) 进行连接。</p>
<p>示例：</p>
<pre class="brush: abap;">

SELECT SINGLE B~BUKRS
   FROM BSEG AS B
   INNER JOIN T001 AS T ON B~BUKRS = T~BUKRS.

</pre>
<p>&nbsp;</p>
<p>其中，<b>INNER JOIN</b> 与 <b>LEFT/RIGHT JOIN</b> 的区别：</p>
<p>INNER JOIN的 ON 条件下，两个表数据必须完全匹配；</p>
<p>LEFT JOIN 的 ON 条件下，左边的表需满足，右边的表不用完全匹配。</p>
<p>3. <b>BYPASSING BUFFER</b>  - 绕过 Application Buffer，直接读取数据库。</p>
<p>4. <b>UP TO n ROWS </b> - 查询n 笔记录，限制读取数据的条数，即只读取查询数据的前5条记录。</p>
<p>&nbsp;<br />
&nbsp;</p>
<p><b>3.6 WHERE </b><b>子句</b></p>
<p>WHERE 关键字后，可带子句包括：</p>
<p>1. <b>&lt;field&gt;</b> <b style="color:red;">OP</b> <b>g</b></p>
<p><b>OP</b>包括：=, EQ, &lt;&gt;, NE, &lt;, LT, &gt;, GT, &lt;, LE, &gt;=, GE, &lt;=, LE等关系操作符</p>
<p>2. <b>&lt;field&gt;</b> <b style="color:red;">[NOT] BETWEEN</b> <b>g1</b> <b style="color:red;">AND</b> <b>g2</b> : 包含 g1 和 g2</p>
<p>&nbsp;</p>
<p>示例：</p>
<pre class="brush: abap;">

* Between 的演示
SELECT carrid 
  INTO TABLE itab 
  FROM scarr 
  WHERE carrid  BETWEEN 'AA'  AND 'AZ'.

LOOP AT ITAB.

   WRITE : / ITAB-CARRID.

ENDLOOP. 

</pre>
<p>&nbsp;</p>
<p>3. <b>&lt;field&gt;</b> <b style="color:red;">[NOT] LIKE</b> <b>g</b> ：通过通配符(‘_’, ’%’)模糊查询</p>
<p>&nbsp;</p>
<p>示例：</p>
<pre class="brush: abap;">

SELECT  * 
   FROM mara   
   WHERE matnr LIKE ‘101-%’.
*  WHERE matnr LIKE ‘101-___’.

WRITE : / mara-matnr.

ENDSELECT. 

</pre>
<p>&nbsp;</p>
<p>这里强烈建议，尽量避免使用LIKE语法作为条件筛选，因为模糊查询的数据量多数时候会非常庞大；</p>
<p>这样会直接消耗系统资源，影响系统性能。</p>
<p>&nbsp;</p>
<p>4. <b>&lt;field&gt;</b> <b style="color:red;">[NOT]  IN</b> <b>( g1, …,  gn )</b> : 多个组合作为筛选条件</p>
<p>5. <b>&lt;field&gt;</b> <b style="color:red;">[NOT] IN</b> <b>itab</b>  : 使用Range table作为筛选条件</p>
<p>6. <b>&lt;field&gt;</b> <b style="color:red;">IS [NOT]</b> <b>NULL</b> ：筛选某字段是否为空</p>
<p>&nbsp;<br />
&nbsp;</p>
<p><b>3.7 <span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/for-all-entries-in" title="FOR ALL ENTRIES " target="_blank">FOR ALL ENTRIES </a></span>IN </b><b>语句</b></p>
<p>由于内表可以临时存储多条数据，而Open SQL允许将内表数据作为查询条件，故可以通过 <b style="color:red;"><span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/for-all-entries-in" title="FOR ALL ENTRIES " target="_blank">FOR ALL ENTRIES </a></span>IN</b><b> </b>语句参照内表作为条件查询。</p>
<p>相当于使用 <b>INNER JOIN</b> 连接两个表一样，然后在数据量庞大的时候，<b><span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/for-all-entries-in" title="FOR ALL ENTRIES IN" target="_blank">FOR ALL ENTRIES IN</a></span> </b><b>语句</b>会比<b>INNER JOIN</b>的查询快捷。</p>
<p>两者各有优缺点，视具体情况而定。</p>
<p>&nbsp;</p>
<p>示例：</p>
<pre class="brush: abap;">

* INNER JOIN 用法：
SELECT DISTINCT knb1~bukrs t001~butxt
    FROM knb1 INNER JOIN t001
    ON knb1~bukrs = t001~bukrs
    INTO CORRESPONDING FIELDS OF TABLE tb_bukrs
    WHERE kunnr IN rn_kunnr.



* FOR ALL ENTRIES IN用法：
SELECT DISTINCT bukrs 
   INTO CORRESPONDING FIELDS OF TABLE gt_knb1
   FROM knb1.
  
IF gt_knb1[] IS NOT INITIAL.
   SELECT DISTINCT butxt FROM t001
      INTO CORRESPONDING FIELDS OF TABLE gt_too1
      FOR ALL ENTRIES IN gt_knb1
      WHERE bukrs = gt_knb1-bukrs.
END IF.

</pre>
<p>&nbsp;</p>
<p>注意事项：</p>
<p>1．使用该语句，对于最后得到的结果集，系统会自动删除重复行。</p>
<p>因此如果你要保留重复行记录时，记得在 <b>SELECT</b> 语句中添加足够键值项目（有必要时，增加全部键值项目），</p>
<p>以保证结果集中所需重复项目不会被删除。</p>
<p>（例如选取支付金额时，支付事件可能不同，但金额可能相同，此时一定要注意，以避免错误删除结果记录。）</p>
<p>2．<b><span class='wp_keywordlink'><a href="http://www.sapjx.com/tag/for-all-entries-in" title="FOR ALL ENTRIES IN" target="_blank">FOR ALL ENTRIES IN</a></span></b> 后面使用的内部表 itab 如果为空，系统将当前 CLIENT 下所有记录选出。</p>
<p>因此为避免无意义的检索，在使用该语句前一定要判断内部表 itab 是否为空，为空时不执行包含该语句的数据库检索处理。</p>
<p>3．由于 itab-f 实际上是作为占位符被替换，所以内部表 itab 中不要包含 HEADER 行（项目标识名称行）,以免造成混淆，检索出错。</p>
<p>4．内部表itab中作为条件替换用项目的类型和长度，一定要和检索数据库中对应的项目相同，否则编译不能通过。</p>
<p>5．对于内部表 itab中 作为条件替换用项目，不能使用 <b>LIKE,BETWEEN,IN</b> 比较操作符。</p>
<p>6．使用该语句时，<b>ORDER BY</b> 语句和 <b>HAVING</b> 语句将不能使用。</p>
<p>7．使用该语句时，除COUNT( * )以外的所有标准合计函数<b>（MAX,MIN,AVG,SUM）</b>都不能使用。</p>
<p>&nbsp;<br />
&nbsp;</p>
<p><b>4. INSERT </b><b>语句</b></p>
<p>INSERT语法结构：</p>
<p><b style="color:red;">INSERT INTO &lt;dbtab&gt; VALUES  wa</b>.</p>
<p><b style="color:red;">INSERT &lt;dbtab&gt; FROM TABLE itab</b>.</p>
<p>示例1：</p>
<pre class="brush: abap;">

TABLES scustom. 
scustom-mandt      = '002'. 
scustom-id         = '12400177'. 
scustom-name       = 'Robinson'. 
scustom-postcode   = '69542'. 
scustom-city       = 'Heidelberg'. 
scustom-custtype   = 'P'. 
scustom-discount   = '003'. 
scustom-telephone  = '01234/56789'. 

INSERT INTO scustom CLIENT SPECIFIED VALUES scustom. 

</pre>
<p>&nbsp;</p>
<p><b>CLIENT SPECIFIED</b>: 是否指定 CLIENT</p>
<p>如果指定CLIENT，则只插入指定CLIENT的相关数据；</p>
<p>如果用在WHERE语句，则是抽取指定CLIENT的相关数据；如若没有指定CLIENT，则抽取所有CLIENT的数据。</p>
<p>&nbsp;</p>
<p>示例2：</p>
<pre class="brush: abap;">

TABLES SPFLI.
DATA ITAB LIKE SPFLI OCCURS 10 WITH HEADER LINE.

ITAB-CARRID = 'UA'. ITAB-CONNID = '0011'. ITAB-CITYFROM = ..
APPEND ITAB.

ITAB-CARRID = 'LH'. ITAB-CONNID = '1245'. ITAB-CITYFROM = ..
APPEND ITAB.

ITAB-CARRID = 'AA'. ITAB-CONNID = '4574'. ITAB-CITYFROM = ..
APPEND ITAB.

................

INSERT SPFLI FROM TABLE ITAB ACCEPTING DUPLICATE KEYS.  
IF SY-SUBRC = 0.

   .....

ELSEIF-SUBRC = 4.

   .....

ENDIF.

</pre>
<p>&nbsp;</p>
<p><b>5.UPDATE</b><b>语句</b></p>
<p><b style="color:red;">1. UPDATE &lt;dbtab&gt; SET f1 &#8230; fn  [WHERE sql_cond] . </b></p>
<p><b style="color:red;">2. UPDATE &lt;dbtab&gt; FROM TABLE itab [WHERE sql_cond] .</b></p>
<p>示例：</p>
<pre class="brush: abap;">

UPDATE scustom SET: DISCOUNT  = '003', 
                    TELEPHONE = '0621/444444' 
       WHERE ID  = '00017777'. 

TABLES scustom. 
DATA wa LIKE scustom. 

wa-id         = '12400177'. 
wa-telephone  = '06201/44889'. 

UPDATE scustom FROM wa.

</pre>
<p>&nbsp;</p>
<p><b>6. MODIFY</b><b>语句</b></p>
<p><b style="color:red;">1.MODIFY  &lt;dbtab&gt;</b>.   （相当于INSERT 和UPDATE）</p>
<p><b style="color:red;">2.MODIFY  &lt;dbtab&gt; FROM TABLE itab</b>.</p>
<p>示例：</p>
<pre class="brush: abap;">

TABLES scustom. 
scustom-id         = '12400177'. 
scustom-name       = 'Robinson'. 
scustom-postcode   = '69542'. 
scustom-city       = 'Heidelberg'. 
scustom-custtype   = 'P'. 
scustom-discount   = '003'. 
scustom-telephone  = '06201/44889'. 

MODIFY scustom.

</pre>
<p>&nbsp;</p>
<p><b>7. DELETE</b><b>语句</b></p>
<p><b style="color:red;">1. DELETE FROM &lt;dbtab&gt; WHERE cond.</b></p>
<p><b style="color:red;">2. DELETE &lt;dbtab&gt; FROM TABLE itab.</b></p>
<p>&nbsp;</p>
<p>Delete 最好有条件限定，谨慎使用，避免误删数据</p>
<pre class="brush: abap;">

DELETE FROM SBOOK WHERE CARRID = 'LH'  AND CONNID = '0400' AND  FLDATE = '19950228'.

</pre>
<p>&nbsp;<br />
&nbsp;</p>